Skip to content

feat(huddle): huddle-transcript signed event kind + SDK builder (#2513)#2522

Open
artile wants to merge 1 commit into
block:mainfrom
artile:feat/huddle-transcript-events
Open

feat(huddle): huddle-transcript signed event kind + SDK builder (#2513)#2522
artile wants to merge 1 commit into
block:mainfrom
artile:feat/huddle-transcript-events

Conversation

@artile

@artile artile commented Jul 23, 2026

Copy link
Copy Markdown

Summary

A huddle's spoken content is the one modality that never becomes part of the event log. Agents watching a channel can see that a huddle happened (lifecycle events), but never what was said. This PR adds the protocol foundation for huddle transcripts as first-class, searchable, signed channel events — the core of #2513.

This is scoped as the reviewable protocol slice: the event kind, a typed reader, an SDK builder, and relay ingest registration — all built and tested. The server-side recording + transcription pipeline is designed below and called out as the honest follow-up, so this PR is small and correct rather than large and unbuildable.

What's in this PR

  • buzz-core/src/kind.rs — new KIND_HUDDLE_TRANSCRIPT = 48104, placed in the huddle lifecycle range (48100–48106) and registered in ALL_KINDS (so the duplicate-detection test covers it).
  • buzz-core/src/huddle.rsHuddleTranscriptSegment, the reader side agents use. A segment's content is plain transcript text (so it is FTS-indexed and readable exactly like a message); structure lives in tags:
    kind:    48104
    content: "the quick brown fox"        ← plain text, FTS-indexed
    tags:    ["h", <channel-uuid>]        ← channel scope (NIP-29 group)
             ["p", <speaker-pubkey>]      ← attribution
             ["e", <huddle-started-id>]   ← links segment → huddle session
             ["start", <ms>] ["end", <ms>]← offsets from huddle start
             ["lang", <bcp47>]            ← optional
    
    from_content_and_tags parses a segment back out (used by agents / any consumer).
  • buzz-sdk/src/builders.rsbuild_huddle_transcript(...), the signed-event builder, with validation (64-hex speaker + huddle id, non-empty text, 16 KB cap, end >= start).
  • buzz-relay/src/handlers/ingest.rs — registers the kind's write scope (ChannelsWrite) and h-channel scoping, alongside the other huddle events.

Why this shape

  • Attribution is free server-side. The audio room already keys every participant by (pubkey, peer_index) (crates/buzz-relay/src/audio/room.rs), so a segment maps to a speaker pubkey with no diarization.
  • Searchable with no new indexer. Because content is plain text, transcripts flow through the existing Postgres FTS search_tsv generated column automatically.
  • Readable by agents like any message — a channel-member agent gets huddle content as ordinary context, closing the "incident memory" gap in the README.

Testing

  • cargo test -p buzz-core -p buzz-sdk huddle9 passed, 0 failed (5 core reader + 4 SDK builder, including a round-trip from the SDK builder through the core reader).
  • cargo check -p buzz-relay (SQLX_OFFLINE) — clean.
  • cargo fmt — clean.
  • Zero unsafe; no panics/unwraps on the new paths.

Follow-ups (the rest of #2513) — designed, not in this PR

  1. Server-side per-speaker recording. Tap the audio room's frame fan-out, buffer Opus per speaker pubkey, and persist to the existing media store (crates/buzz-media, S3/MinIO/Blossom) on huddle end. Needs a memory/retention budget per room.
  2. Transcription over per-speaker tracks. A pluggable/off-box transcriber runs per track; because segments are already attributed, output is "speaker → text → time range" with no speaker-separation. A multilingual model (e.g. Parakeet v3, see Huddle transcription is English-only: non-English speech (e.g. Korean) produces garbled transcripts #2478 / PR feat(huddle): make STT model selectable + add multilingual Parakeet v3 (#2478) #2520) makes this multilingual by construction.
  3. Consent + retention. Recording is opt-in per huddle and announced via KIND_HUDDLE_GUIDELINES (48106) as a "this huddle is recorded" policy; a NIP-09 deletion path removes transcript events + their audio objects.

Splitting these keeps each PR reviewable; this one lands the protocol contract they all build on.

Refs #2513

…lock#2513)

A huddle's spoken content is the one modality that never becomes part of the
event log — agents watching a channel can see that a huddle happened, but never
what was said (block#2513). This adds the protocol foundation for huddle transcripts
as first-class, searchable, signed channel events.

- buzz-core/kind.rs: new `KIND_HUDDLE_TRANSCRIPT = 48104`, in the huddle
  lifecycle range and registered in `ALL_KINDS`.
- buzz-core/huddle.rs: `HuddleTranscriptSegment` — the typed reader side used
  by agents. A segment's *content is plain transcript text* (so it is
  FTS-indexed and readable like a message); structure is in tags: `h` channel,
  `p` speaker pubkey, `e` huddle-started event, `start`/`end` millisecond
  offsets, optional `lang`. `from_content_and_tags` parses one back out.
- buzz-sdk/builders.rs: `build_huddle_transcript(...)` constructs the signed
  event with validation (hex speaker/huddle id, non-empty text, size cap,
  end >= start).
- buzz-relay/ingest.rs: register the kind's write scope (`ChannelsWrite`) and
  `h`-channel scoping, alongside the other huddle events.

Per-speaker attribution is inherent server-side: the audio room already keys
participants by `(pubkey, peer_index)`, so a segment maps to a pubkey with no
diarization. Because content is plain text, transcripts are searchable through
the existing Postgres FTS `search_tsv` column with no extra indexer.

Scope: this is the reviewable protocol slice. Server-side per-speaker recording
(tap the audio room -> persist Opus per pubkey to buzz-media), the transcription
pass over those tracks, and consent/retention (opt-in per huddle announced via
KIND_HUDDLE_GUIDELINES; NIP-09 deletion for transcript events + object cleanup)
are the documented follow-ups — see the PR body.

Tested: buzz-core (5) + buzz-sdk (4) huddle tests pass, incl. a round-trip from
the SDK builder through the core reader. Zero unsafe, no panics on new paths.

Refs block#2513

Signed-off-by: Taras Kornichuk <dread9ko@gmail.com>
@artile
artile force-pushed the feat/huddle-transcript-events branch from 52d5d0e to b99a327 Compare July 23, 2026 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant